home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NOVA - For the NeXT Workstation
/
NOVA - For the NeXT Workstation.iso
/
Apps
/
AudioApps
/
GISO
/
Subprocess.m
< prev
next >
Wrap
Text File
|
1992-12-20
|
8KB
|
388 lines
/*----------------------------------------------------------------------------
Subprocess.m
From Subprocess example by Charles L. Oei
pty support by Joe Freeman
with encouragement from Kristofer Younger
Subprocess Example, Release 2.0
NeXT Computer, Inc.
You may freely copy, distribute and reuse the code in this example.
NeXT disclaims any warranty of any kind, expressed or implied, as to
its fitness for any particular use.
SYNOPSIS
Handles a UNIX process that runs asynchronously.
REVISIONS
Subprocess.m,v
# Revision 1.1 1992/07/04 03:17:22 nwc
# Initial revision
#
----------------------------------------------------------------------------*/
#import <sys/wait.h>
#import <sys/resource.h>
#import <appkit/nextstd.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
#import "Subprocess.h"
extern int wait4(int, union wait *, int, struct rusage *);
static void fdHandler(int theFd, id self);
static void stderrFdHandler(int theFd, id self);
#define PIPE_ERROR "Error starting UNIX pipes to subprocess."
#define VFORK_ERROR "Error starting UNIX vfork of subprocess."
@interface Subprocess(Private)
- childDidExit;
- fdHandler:(int)theFd;
@end
@implementation Subprocess(Private)
/*
* cleanup after a child process exits
*/
- childDidExit
{
union wait w;
int status = 0;
int thePid;
thePid = wait4(childPid, &w, WUNTRACED, NULL);
#ifdef DEBUG
fprintf(stderr, "%s: wait4() on process id #%d returned %d, with "
"w_status = %d, w_retcode = %d, w_stopval = %d, "
"w_stopsig = %d, w_termsig = %d\n",
[self name], childPid, thePid, w.w_status, w.w_retcode,
w.w_stopval, w.w_stopsig, w.w_termsig);
#endif
if (thePid > 0)
{
if (WIFEXITED(w))
status = (w.w_status >> 8);
else
{
if (WIFSTOPPED(w))
status = SUBPROCESS_STOPPED;
else
{
if (WIFSIGNALED(w))
status = SUBPROCESS_SIGNALED;
}
}
DPSRemoveFD(fromChild);
DPSRemoveFD(stderrFromChild);
fclose(fpFromChild);
close(fromChild);
close(stderrFromChild);
fclose(fpToChild);
running = NO;
[delegate perform:@selector(subprocess:done:)
with :self
with:(void *)status];
}
return (self);
}
/*
* DPS handler for output from subprocess
*/
- fdHandler:(int)theFd
{
char *s, *linep;
int bufferCount;
bufferCount = read(theFd, outputBuffer + outputBufferLen,
BUFSIZ - outputBufferLen);
if (bufferCount <= 0)
{
[self childDidExit];
return (self);
}
outputBuffer[bufferCount + outputBufferLen] = '\0';
/*
* Send lines in the buffer to the delegate
*/
s = linep = outputBuffer;
while (s != NULL)
{
if ((s = index(linep, '\n')) != NULL)
{
*s = (char)0;
[delegate perform:@selector(subprocess:output:)
with :self
with:(void *)linep];
linep = s + 1;
}
}
/*
* Copy the last part of the line back into the input buffer for next time (incomplete line)
*/
outputBufferLen = strlen(linep);
strncpy(outputBuffer, linep, outputBufferLen);
return (self);
}
@end
@implementation Subprocess
/*
* Cover for the init:withDelegate: with a nil delegate
*/
- init:(const char *)subprocessString
{
return ([self init:subprocessString
withDelegate:nil]);
}
- init:(const char *)subprocessString withDelegate:theDelegate
{
int pipeTo[2];
int pipeFrom[2];
int pipeStderr[2]; /* for stderr to different fd */
int numFds, fd;
int processGroup;
[super init];
outputBufferLen = 0;
stderrBufferLen = 0;
[self setDelegate:theDelegate];
if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0 || pipe(pipeStderr) < 0)
{
[delegate perform:@selector(subprocess:error:)
with :self
with:(void *)PIPE_ERROR];
return (self);
}
switch (childPid = vfork())
{
case -1: /* error */
[delegate perform:@selector(subprocess:error:)
with :self
with:(void *)VFORK_ERROR];
return (self);
case 0: /* child */
dup2(pipeTo[0], 0);
dup2(pipeFrom[1], 1); /* get stdout from process */
dup2(pipeStderr[1], 2); /* get stderr here */
numFds = getdtablesize();
for (fd = 3; fd < numFds; fd++)
close(fd);
processGroup = getpid();
ioctl(0, TIOCSPGRP, (char *)&processGroup);
setpgrp(0, processGroup);
/*
* we exec a /bin/sh so that cmds are easier to specify for the user
*/
execl("/bin/sh", "sh", "-c", subprocessString, 0);
perror("vfork (child)"); /* should never gets here tho */
exit(1);
default: /* parent */
running = YES;
close(pipeTo[0]);
close(pipeFrom[1]);
close(pipeStderr[1]);
fpToChild = fdopen(pipeTo[1], "w");
fromChild = pipeFrom[0];
fpFromChild = fdopen(pipeFrom[0], "r");
stderrFromChild = pipeStderr[0];
/*
* Set buffering method, also make it use its own buffers
*/
setbuf(fpToChild, NULL); /* no buffering */
setbuf(fpFromChild, NULL);
DPSAddFD(fromChild, (DPSFDProc) fdHandler, (id) self,
NX_MODALRESPTHRESHOLD + 1);
DPSAddFD(stderrFromChild, (DPSFDProc) stderrFdHandler, (id) self,
NX_MODALRESPTHRESHOLD + 1);
return (self);
}
}
- stderrFdHandler:(int)theFd
{
char *s, *linep;
int bufferCount;
bufferCount = read(theFd, stderrBuffer + stderrBufferLen,
BUFSIZ - stderrBufferLen);
if (bufferCount <= 0)
return (self);
stderrBuffer[bufferCount + stderrBufferLen] = '\0';
/*
* Send lines in the buffer to the delegate
*/
s = linep = stderrBuffer;
while (s != NULL)
{
if ((s = index(linep, '\n')) != NULL)
{
*s = (char)0;
[delegate perform:@selector(subprocess:stderrOutput:)
with :self
with:(void *)linep];
linep = s + 1;
}
}
/*
* Copy the last part of the line back into the input buffer for next time (incomplete line)
*/
stderrBufferLen = strlen(linep);
strncpy(stderrBuffer, linep, stderrBufferLen);
return (self);
}
- send:(const char *)string withNewline:(BOOL) wantNewline
{
fputs(string, fpToChild);
if (wantNewline)
fputc('\n', fpToChild);
return (self);
}
- send:(const char *)string
{
[self send:string withNewline:YES];
return (self);
}
/*
* Returns the process id of the process (and therefore the process group
* of the job)
*/
- (int)pid
{
return (childPid);
}
- (BOOL) isPaused
{
return (paused);
}
- resume:sender
{
if (paused)
{
killpg(childPid, SIGCONT); /* resume the process group */
paused = NO;
}
return (self);
}
- pause:sender
{
if (!paused)
{
killpg(childPid, SIGSTOP); /* pause the process group */
paused = YES;
}
return (self);
}
- (BOOL) isRunning
{
return (running);
}
- terminate:sender
{
if (running)
killpg(childPid, SIGKILL);
return (self);
}
/*
* effectively sends an EOF to the child process stdin
*/
- terminateInput
{
fclose(fpToChild);
return (self);
}
- setDelegate:anObject
{
delegate = anObject;
return (self);
}
- delegate
{
return (delegate);
}
@end
@implementation Object(SubprocessDelegate)
- subprocess: sender done:(int)exitStatus
{
return (self);
}
- subprocess:sender output:(char *)buffer
{
return (self);
}
- subprocess:sender stderrOutput:(char *)buffer
{
return (self);
}
- subprocess:sender error:(const char *)errorString
{
if (NXApp)
NXRunAlertPanel(0, errorString, 0, 0, 0);
else
perror(errorString);
return (self);
}
@end
/*
* And standard error from subprocess
*/
static void stderrFdHandler(int theFd, id self)
{
[self stderrFdHandler:theFd];
}
/*
* DPS handler for output from subprocess
*/
static void fdHandler(int theFd, id self)
{
[self fdHandler:theFd];
}